#include "OiPCH.hpp"
#include <QtCharts/QDateTimeAxis>

using namespace QtCharts;

namespace Oi
{
    namespace PLScannerCore
    {
        class DynamicSplineViewImpl : public Impl<DynamicSplineView>
        {
        public:
            void init(const QStringList);
            void exit();

            void updateChartValues();
            void updateLinePos();

        public:
            DynamicSplineChart* _chart;
            QGraphicsLineItem* _standardLine;
            QGraphicsLineItem* _upperLine;
            QGraphicsLineItem* _lowerLine;
            QGraphicsScene* _scene;

        public:
            bool _isDrag;
            QPoint _dragPoint;

        public:
            bool _isRunning;

        public:
            QTimer _timer;

        public:
            double _lowerLimit;
            double _upperLimit;

        public:
            QWidget* _legend;
            QHBoxLayout* _legendLayout;
            int _legendWidth;

        public:
            QString _formular;
        };

        void DynamicSplineViewImpl::init(const QStringList spline)
        {
            OI_Q(DynamicSplineView);

            _isDrag = false;

            _isRunning = false;

            _lowerLimit = -1.0;
            _upperLimit = -1.0;

            _legendWidth = 0.0;

            _timer.setInterval(10000);
            _timer.setSingleShot(true);

            _scene = new QGraphicsScene();
            _scene->setBackgroundBrush(PLScannerStyle::ChartViewerBgColor());
            q->setScene(_scene);
            q->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
            q->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

            _chart = new DynamicSplineChart(spline);

            _legend = new QWidget(q);
            _legendLayout = new QHBoxLayout(_legend);
            for (auto pl : _chart->legends())
            {
                _legendLayout->addWidget(pl);
                _legendWidth += 150.0;
                q->connect(pl, &QCheckBox::stateChanged, _chart, &DynamicSplineChart::onLegendChecked);
            }

            _standardLine = new QGraphicsLineItem();
            _standardLine->setPen(QPen(Qt::green, 1, Qt::DotLine));

            _upperLine = new QGraphicsLineItem();
            _upperLine->setPen(QPen(Qt::red, 1, Qt::DotLine));

            _lowerLine = new QGraphicsLineItem();
            _lowerLine->setPen(QPen(Qt::red, 1, Qt::DotLine));

            _scene->addItem(_chart);
            _scene->addItem(_standardLine);
            _scene->addItem(_upperLine);
            _scene->addItem(_lowerLine);

            _scene->setSceneRect(1.0 * INT_MIN, 1.0 * INT_MIN, 2.0 * INT_MAX, 2.0 * INT_MAX);

            q->setRenderHint(QPainter::Antialiasing);
            q->setDragMode(QGraphicsView::RubberBandDrag);
            q->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);

            q->connect(&_timer, &QTimer::timeout, q, &DynamicSplineView::onTimeout);
            q->connect(_chart, &DynamicSplineChart::flash, q, &DynamicSplineView::onChartFlash);
        }

        void DynamicSplineViewImpl::exit()
        {
        }

        void DynamicSplineViewImpl::updateChartValues()
        {
            _chart->setValues(OI_SELF()->loadData(( (QDateTimeAxis*)_chart->axisX() )->min(),
                ( (QDateTimeAxis*)_chart->axisX() )->max()));

            _chart->yAxisFittness(DynamicSplineChart::FitToData);

            updateLinePos();
        }

        void DynamicSplineViewImpl::updateLinePos()
        {
            if (_lowerLimit != -1.0 || _upperLimit != -1.0)
            {
                auto p1InChart = QPointF(((QDateTimeAxis*)_chart->axisX())->min().toMSecsSinceEpoch() , (_lowerLimit + _upperLimit ) / 2);
                auto p2InChart = QPointF(((QDateTimeAxis*)_chart->axisX())->max().toMSecsSinceEpoch() , (_lowerLimit + _upperLimit ) / 2);
                auto p1Pos = _chart->mapToPosition(p1InChart);
                auto p2Pos = _chart->mapToPosition(p2InChart);
                auto p1InScene = _chart->mapToScene(p1Pos);
                auto p2InScene = _chart->mapToScene(p2Pos);

                _standardLine->setLine(QLineF(p1InScene, p2InScene));

                p1InChart = QPointF(( (QDateTimeAxis*)_chart->axisX() )->min().toMSecsSinceEpoch(), _lowerLimit );
                p2InChart = QPointF(( (QDateTimeAxis*)_chart->axisX() )->max().toMSecsSinceEpoch(), _lowerLimit);
                p1Pos = _chart->mapToPosition(p1InChart);
                p2Pos = _chart->mapToPosition(p2InChart);
                p1InScene = _chart->mapToScene(p1Pos);
                p2InScene = _chart->mapToScene(p2Pos);

                _lowerLine->setLine(QLineF(p1InScene, p2InScene));

                p1InChart = QPointF(( (QDateTimeAxis*)_chart->axisX() )->min().toMSecsSinceEpoch(), _upperLimit);
                p2InChart = QPointF(( (QDateTimeAxis*)_chart->axisX() )->max().toMSecsSinceEpoch(), _upperLimit);
                p1Pos = _chart->mapToPosition(p1InChart);
                p2Pos = _chart->mapToPosition(p2InChart);
                p1InScene = _chart->mapToScene(p1Pos);
                p2InScene = _chart->mapToScene(p2Pos);

                _upperLine->setLine(QLineF(p1InScene, p2InScene));

            }
        }

        DynamicSplineView::DynamicSplineView(const QStringList splines, QWidget *parent /*= Q_NULLPTR*/)
            : QGraphicsView(parent)
        {
            OI_I(DynamicSplineView)->init(splines);
        }

        DynamicSplineView::~DynamicSplineView()
        {
            OI_E(DynamicSplineView)->exit();
        }

        void DynamicSplineView::zoomFit()
        {
            OI_F(DynamicSplineView);
            d->_chart->setGeometry(0, 0, viewport()->width(), viewport()->height());
            fitInView(d->_chart);
        }

        void DynamicSplineView::setSplines(QStringList splines)
        {
            OI_F(DynamicSplineView);
            if (splines == d->_chart->splines())
            {
                return;
            }

            delete d->_chart;

            delete d->_legendLayout;

            d->_legendWidth = 0.0;

            d->_chart = new DynamicSplineChart(splines);

            d->_legendLayout = new QHBoxLayout(d->_legend);
            for(auto pl : d->_chart->legends())
            {
                d->_legendLayout->addWidget(pl);
                d->_legendWidth += 150.0;
                connect(pl, &QCheckBox::stateChanged, d->_chart, &DynamicSplineChart::onLegendChecked);
            }

            d->_scene->addItem(d->_chart);
            connect(d->_chart, &DynamicSplineChart::flash, this, &DynamicSplineView::onChartFlash);

            zoomFit();
        }

        QStringList DynamicSplineView::splines() const
        {
            return OI_IMPL()->_chart->splines();
        }

        DynamicSplineChart* DynamicSplineView::chart()
        {
            return OI_IMPL()->_chart;
        }

        void DynamicSplineView::setLimit(const double upper, const double lower)
        {
            OI_F(DynamicSplineView);

            d->_chart->axisY()->setRange(1.1 * d->_lowerLimit - 0.1 * d->_upperLimit,
                1.1 * d->_upperLimit - 0.1 * d->_lowerLimit);

            d->_chart->setLimit(upper, lower);

            d->_lowerLimit = lower;
            d->_upperLimit = upper;

            d->updateLinePos();
        }

        void DynamicSplineView::run(bool flag)
        {
            OI_F(DynamicSplineView);

            d->_isRunning = flag;

            d->_chart->run(flag);
        }

        void DynamicSplineView::toCurrent()
        {
            OI_F(DynamicSplineView);

            auto dt = QDateTime::currentDateTime();
            d->_chart->axisX()->setRange(dt.addSecs(-d->_chart->xRange()), dt);

            d->_chart->setValues(loadData(dt.addSecs(-d->_chart->xRange()), dt));

            d->_chart->yAxisFittness();

            d->updateLinePos();
        }

        void DynamicSplineView::update()
        {
            OI_F(DynamicSplineView);
            d->updateChartValues();
        }

        void DynamicSplineView::setFormular(const QString name)
        {
            OI_IMPL()->_formular = name;
        }

        QString DynamicSplineView::formular() const
        {
            return OI_IMPL()->_formular;
        }

        void DynamicSplineView::resizeEvent(QResizeEvent *event)
        {
           OI_F(DynamicSplineView);

           zoomFit();

           d->_legend->setGeometry((width() - d->_legendWidth) / 2, 0, d->_legendWidth, 35);
        }

        void DynamicSplineView::mousePressEvent(QMouseEvent *event)
        {
            OI_F(DynamicSplineView);
            if(event->button() & Qt::MiddleButton)
            {
                setCursor(Qt::OpenHandCursor);
                d->_isDrag = true;
                d->_dragPoint = event->pos();

                d->_chart->run(false);

                event->accept();
                return;
            }

            QGraphicsView::mousePressEvent(event);
        }

        void DynamicSplineView::mouseMoveEvent(QMouseEvent* event)
        {
            OI_F(DynamicSplineView);

            if( event->buttons() & Qt::MiddleButton )
            {
                if(d->_isDrag)
                {
                    d->_chart->scroll(d->_dragPoint.x() - event->x(), event->y() - d->_dragPoint.y());
                    d->_dragPoint = event->pos();

                    //d->_chart->updatePlotAreaBackground();

                    d->updateChartValues();

                    event->accept();

                    return;
                }
            }

            QGraphicsView::mouseMoveEvent(event);
        }

        void DynamicSplineView::mouseReleaseEvent(QMouseEvent* event)
        {
            OI_F(DynamicSplineView);

            if(event->button() & Qt::MiddleButton)
            {
                setCursor(Qt::ArrowCursor);
                d->_isDrag = false;

                if (d->_isRunning)
                {
                    d->_timer.start();
                }

                event->accept();
                return;
            }

            QGraphicsView::mouseReleaseEvent(event);
        }

        void DynamicSplineView::mouseDoubleClickEvent(QMouseEvent* event)
        {
            OI_F(DynamicSplineView);

            if (event->button() & Qt::MouseEventCreatedDoubleClick)
            {
                d->_timer.stop();
                onTimeout();
            }

            QGraphicsView::mouseDoubleClickEvent(event);
        }

        void DynamicSplineView::paintEvent(QPaintEvent *event)
        {
            QGraphicsView::paintEvent(event);
        }

        void DynamicSplineView::wheelEvent(QWheelEvent *event)
        {
            OI_F(DynamicSplineView);

            d->_chart->run(false);

            if(event->delta() > 0)
            {
                d->_chart->zoom(1.1);
            }
            else
            {
                d->_chart->zoom(10.0 / 11);
            }

            //d->_chart->updatePlotAreaBackground();

            d->updateChartValues();

            if(d->_isRunning)
            {
                d->_timer.start();
            }
        }

        void DynamicSplineView::onTimeout()
        {
            OI_F(DynamicSplineView);

            d->_chart->setValues(loadData(QDateTime::currentDateTime().addSecs(-d->_chart->xRange()),
                QDateTime::currentDateTime()));

            d->_chart->yAxisFittness(DynamicSplineChart::FitToLimite);

            d->updateLinePos();

            d->_chart->run(true);
        }

        void DynamicSplineView::onChartFlash()
        {
            OI_F(DynamicSplineView);

            d->updateLinePos();
        }

    }
}